/*-*-C-*-
 *
 * ResEd Window CSE: main
 */

#include "resed.h"
#include "main.h"

#include "swicall.h"
#include "wimp.h"
#include "resformat.h"
#include "newmsgs.h"
#include "focus.h"
#include "interactor.h"
#include "menu.h"
#include "registry.h"

#include "format.h"
#include "relocate.h"
#include "windowedit.h"
#include "align.h"
#include "colours.h"
#include "coords.h"
#include "extent.h"
#include "gadgdefs.h"
#include "gadget.h"
#include "grid.h"
#include "help.h"
#include "keycuts.h"
#include "props.h"
#include "protocol.h"
#include "toolbars.h"
#include "windinfo.h"


/* Globals */

char appdir[FILENAMELEN];       /* contents of Resed$Window$Dir */
int taskhandle;
int parenttaskhandle;
MsgRec msgs;                    /* global messages file */

int wimpversion;                /* eg 350 */



/* Statics */

static unsigned int eventmask = (BIT(EV_NULL_REASON_CODE) |
                                 BIT(EV_POINTER_LEAVING_WINDOW) |
                                 BIT(EV_POINTER_ENTERING_WINDOW) );


/*
 * Crank up the Wimp
 */

static error * start_wimp ()
{
    int task;
    error *err;
    char *taskname = message_lookup(&msgs, "TaskName");
    int messages[] = {

        MESSAGE_DRAG_CLAIM,
        MESSAGE_DRAGGING,
        MESSAGE_CLAIM_ENTITY,

        MESSAGE_MENUS_DELETED,
        MESSAGE_MENU_WARNING,

        MESSAGE_DATA_SAVE,
        MESSAGE_DATA_SAVE_ACK,
        MESSAGE_DATA_LOAD,
        MESSAGE_DATA_LOAD_ACK,

        MESSAGE_RAM_FETCH,
        MESSAGE_RAM_TRANSMIT,

        MESSAGE_TASK_CLOSE_DOWN,
        MESSAGE_HELP_REQUEST,
        MESSAGE_MODE_CHANGE,
        MESSAGE_FONT_CHANGED,

        MESSAGE_RESED_OBJECT_LOAD,
        MESSAGE_RESED_OBJECT_LOADED,
        MESSAGE_RESED_OBJECT_SENDING,
        MESSAGE_RESED_OBJECT_SEND,
        MESSAGE_RESED_OBJECT_RENAMED,
        MESSAGE_RESED_OBJECT_DELETED,
        MESSAGE_RESED_OBJECT_NAME,
        MESSAGE_RESED_SPRITES_CHANGED,

        0 };

    if (taskname == NULL)
        taskname = "ResEd Window Editor";         /* fallback */
    strncpy((char *)&task, "TASK", 4);
    err = swi (Wimp_Initialise,
               R0, 310,
               R1, task,
               R2, taskname,
               R3, messages,
               OUT,
               R1, &taskhandle,  END);
    return err;
}


/* 
 * Read in window prototypes for this module and others.
 */

static error * load_prototypes (void)
{
    char name[FILENAMELEN];
    strcpy(name, appdir);
    strcat(name, ".Templates");

    ER ( swi(Wimp_OpenTemplate, R1, name, END) );

    ER ( windowedit_load_prototypes() );
    ER ( windinfo_load_prototypes() );
    ER ( props_load_prototypes() );
    ER ( colours_load_prototypes() );
    ER ( extent_load_prototypes() );
    ER ( keycuts_load_prototypes() );
    ER ( toolbars_load_prototypes() );
    ER ( grid_load_prototypes() );
    ER ( coords_load_prototypes() );

    /* load prototypes for each gadget type */
    {
        GadgetDefPtr def = gadgetdefs;

        while ( def->type != -1)
        {
            ER ( wimp_load_template_returning_size (def->templatename,
                                                    &def->proto,
                                                    &def->protosize) );
            def++;
        }
    }

    /* and for the "unknown gadget type" */
    ER ( wimp_load_template_returning_size (unkgadgetdef.templatename,
                                            &unkgadgetdef.proto,
                                            &unkgadgetdef.protosize) );

    ER ( swi(Wimp_CloseTemplate, END) );

    return NULL;
}


/*
 * Respond to mouse click events in any window
 */

static error * mouse_click (int *buf)
{
    MouseClickPtr mouse = (MouseClickPtr) buf;
    unsigned int modifiers = wimp_read_modifiers ();
    void *closure;
    RegistryType type = registry_lookup_window(mouse->windowhandle, &closure);
    switch (type)
    {

    case WindowEdit:
        return windowedit_mouse_click (mouse, modifiers, (WindowObjPtr) closure);
    case MainpropsDbox:
        return props_main_dbox_mouse_click (mouse, modifiers, (WindowObjPtr) closure);
    case OtherpropsDbox:
        return props_other_dbox_mouse_click (mouse, modifiers, (WindowObjPtr) closure);
    case ColoursDbox:
        return colours_dbox_mouse_click (mouse, modifiers, (WindowObjPtr) closure);
    case ExtentDbox:
        return extent_dbox_mouse_click (mouse, modifiers, (WindowObjPtr) closure);
    case ShortcutsDbox:
        return keycuts_dbox_mouse_click (mouse, modifiers, (WindowObjPtr) closure);
    case ShortcutsPane:
        return keycuts_pane_mouse_click (mouse, modifiers, (WindowObjPtr) closure);
    case ToolbarsDbox:
        return toolbars_dbox_mouse_click (mouse, modifiers, (WindowObjPtr) closure);
    case GadgetDbox:
        return gadget_dbox_mouse_click (mouse, modifiers, (GadgetPtr) closure);
    }
    return NULL;
}



/*
 * Handle keypresses.  ESCAPE cancels the current foreground interaction.
 * Other keys may be handled by individual window types, and unknown ones
 * are passed back to the Wimp.
 */

static error * key_pressed (int *buf)
{
    KeyPressPtr key = (KeyPressPtr) buf;
    void *closure;
    Bool consumed = FALSE;

    /* offer ESCAPE to any interactor first */
    if (key->code == 0x1b && interactor_active ())
    {
        interactor_cancel ();   /* Kill current drag, lassoo etc */
        consumed = TRUE;
    }
    else
    {
        switch (registry_lookup_window (key->caret.windowhandle, &closure))
        {

        case WindowEdit:
            ER ( windowedit_key_pressed ((WindowObjPtr) closure, key, &consumed) );
            break;
        case MainpropsDbox:
            ER ( props_main_dbox_key_pressed ((WindowObjPtr) closure, key, &consumed) );
            break;
        case OtherpropsDbox:
            ER ( props_other_dbox_key_pressed ((WindowObjPtr) closure, key, &consumed) );
            break;
        case ColoursDbox:
            ER ( colours_dbox_key_pressed ((WindowObjPtr) closure, key, &consumed) );
            break;
        case ExtentDbox:
            ER ( extent_dbox_key_pressed ((WindowObjPtr) closure, key, &consumed) );
            break;
        case ShortcutsDbox:
            ER ( keycuts_dbox_key_pressed ((WindowObjPtr) closure, key, &consumed) );
            break;
        case ToolbarsDbox:
            ER ( toolbars_dbox_key_pressed ((WindowObjPtr) closure, key, &consumed) );
            break;
        case GadgetDbox:
            ER ( gadget_dbox_key_pressed ((GadgetPtr) closure, key, &consumed) );
            break;

        }
    }

    if (!consumed)
    {
        ER ( swi (Wimp_ProcessKey,  R0, key->code,  END) );
    }
    return NULL;
}


/*
 * Respond to all message events.  Rather annoyingly, we can't determine the
 * window/icon pair without reference to the message type, so the test
 * for the type is here rather than in the window-specific procs.
 */

static error * message (int event, int *buf)
{
    MessageHeaderPtr hdr = (MessageHeaderPtr) buf;
    Bool protocol;

    ER ( protocol_incoming_message (event, buf, &protocol) );

    if (protocol)
        return NULL;            /* handled by protocol message dispatcher */

    switch (event)
    {
    case EV_USER_MESSAGE:
    case EV_USER_MESSAGE_RECORDED:
        switch (hdr->messageid)
        {
        case MESSAGE_QUIT:
            error_exit (0);
        case MESSAGE_TASK_CLOSE_DOWN:
            block
            {
/*                dprintf ("MENU: Received TASK_CLOSE_DOWN %d (shell is %d)\n",
                         hdr->taskhandle, parenttaskhandle); */
                if (hdr->taskhandle == parenttaskhandle)
                    error_exit (0);
            }
            break;
        case MESSAGE_HELP_REQUEST:
            return help_message ((MessageHelpRequestPtr) buf);

        case MESSAGE_DATA_SAVE:
            return protocol_send_resed_object_name_request ((MessageDataSavePtr) buf);

        case MESSAGE_MODE_CHANGE:
            return wimp_examine_mode ();

        case MESSAGE_CLAIM_ENTITY:
            return focus_message_claim_entity ((MessageClaimEntityPtr) buf);
        }
        break;
    case EV_USER_MESSAGE_ACKNOWLEDGE:
        switch (hdr->messageid)
        {
        }
        break;
    }
    return NULL;
}


/*
 * Respond to any open window request events
 */

static error * open_window_request (int *buf)
{
    WindowPtr win = (WindowPtr) buf;    /* PUN; it's only half there... */
    void *closure;
    RegistryType type = registry_lookup_window(win->handle, &closure);

    switch (type)
    {

    case WindowEdit:
        return windowedit_reopen_window (win, (WindowObjPtr)closure);
    case WindInfo:
        return windinfo_open_window (win);
    case MainpropsDbox:
        return props_reopen_main_dbox (win, (WindowObjPtr) closure);
    case OtherpropsDbox:
        return props_reopen_other_dbox (win, (WindowObjPtr) closure);
    case ColoursDbox:
        return colours_reopen_dbox (win, (WindowObjPtr) closure);
    case ExtentDbox:
        return extent_reopen_dbox (win, (WindowObjPtr) closure);
    case ShortcutsDbox:
        return keycuts_reopen_dbox (win, (WindowObjPtr) closure);
    case ShortcutsPane:
        return keycuts_reopen_pane (win, (WindowObjPtr) closure);
    case ToolbarsDbox:
        return toolbars_reopen_dbox (win, (WindowObjPtr) closure);
    case GadgetDbox:
        return gadget_reopen_dbox (win, (GadgetPtr) closure);

    }
    return NULL;
}


/*
 * Respond to any close window request events.  Strictly speaking
 * we don't need to handle this for dboxes (which don't have close
 * icons normally).  Having the code is worthwhile because it lets
 * someone add close icons later if they want them.
 */

static error * close_window_request (int *buf)
{
    WindowPtr win = (WindowPtr) buf;    /* PUN; it's only half there... */
    void *closure;
    RegistryType type = registry_lookup_window(win->handle, &closure);

    switch (type)
    {

    case WindowEdit:
        return windowedit_close_wind ((WindowObjPtr)closure);
    case MainpropsDbox:
        return props_close_main_dbox ((WindowObjPtr) closure);
    case OtherpropsDbox:
        return props_close_other_dbox ((WindowObjPtr) closure);
    case ColoursDbox:
        return colours_close_dbox ((WindowObjPtr) closure);
    case ExtentDbox:
        return extent_close_dbox ((WindowObjPtr) closure);
    case ShortcutsDbox:
        return keycuts_close_dbox ((WindowObjPtr) closure);
    case ToolbarsDbox:
        return toolbars_close_dbox ((WindowObjPtr) closure);
    case GadgetDbox:
        return gadget_close_dbox ((GadgetPtr) closure);

    }
    return NULL;
}


/*
 * Respond to any redraw window request events
 */

static error * redraw_window_request (int *buf)
{
    WindowRedrawPtr redraw = (WindowRedrawPtr) buf;
    void *closure;
    RegistryType type = registry_lookup_window(redraw->handle, &closure);
    
    switch (type)
    {
    case WindowEdit:
        ER ( windowedit_redraw_window (redraw, (WindowObjPtr)closure) );
        break;
    case ShortcutsPane:
        ER ( keycuts_redraw_pane (redraw, (WindowObjPtr)closure) );
        break;
    }
    return NULL;
}


static void usage ()
{
    fprintf (stderr, "%s\n", message_lookup(&msgs, "Usage"));
    error_exit (0);
}


/*
 * Get next Wimp event into *event, details filled in buf
 */

error * main_next_event (int *event, int *buf)
{
    unsigned int mask = eventmask & interactor_event_mask();

    if (mask & BIT(EV_NULL_REASON_CODE)) /* NULL events disabled */
    {
        ER ( swi (Wimp_Poll,  R0, mask,  R1, buf,  OUT,  R0, event,  END) );
    }
    else
    {
        int after;
        ER ( swi (OS_ReadMonotonicTime,  OUT,  R0, &after,  END) );
        after += interactor_timeout();
        ER ( swi (Wimp_PollIdle,  R0, mask,  R1, buf,  R2, after,  OUT,  R0, event,  END) );
    }
    return NULL;
}


/*
 * Act on a Wimp event
 */

error * main_dispatch_event (int event, int *buf)
{
    Bool consumed;

    ER ( interactor_event (event, buf, &consumed) );

    if (!consumed)
        switch (event)
        {
        case EV_REDRAW_WINDOW_REQUEST:
            ER ( redraw_window_request (buf) );
            break;
        case EV_OPEN_WINDOW_REQUEST:
            ER ( open_window_request (buf) );
            break;
        case EV_CLOSE_WINDOW_REQUEST:
            ER ( close_window_request (buf) );
            break;
        case EV_MOUSE_CLICK:
            ER ( mouse_click(buf) );
            break ;
        case EV_KEY_PRESSED:
            ER ( key_pressed(buf) );
            break;
        case EV_USER_MESSAGE:
        case EV_USER_MESSAGE_RECORDED:
        case EV_USER_MESSAGE_ACKNOWLEDGE:
            ER ( message(event, buf) );
            break;
        }
    return NULL;
}


/*
 * Main
 */

int main (int argc, char **argv)
{
    int buf[64], event;

    strcpy(appdir, getenv(APPDIR));   /* want to copy this before Wimp_Poll
                                         called */
    EE ( message_openfile (&msgs, "<" APPDIR ">.Messages", 256) );
                                      /* APPDIR can't have changed yet */
    debug_file("<" APPDIR ">.logfile");

    if (argc == 2)
        parenttaskhandle = atoi (argv[1]);
    else
        usage();

/*    dprintf ("WINDOW: parenttaskhandle = %d\n", parenttaskhandle); */

    EE ( wimp_examine_mode() );
    EE ( start_wimp () );
    EE ( load_prototypes () );

    /* determine wimp version */
    EE ( swi (Wimp_ReadSysInfo, R0, 7, OUT, R0, &wimpversion, END) );

    /* Main loop */

    while (1)
    {
        EE ( main_next_event (&event, buf) );
        ED ( main_dispatch_event (event, buf) );
    }
}
